#pragma once

/*--------------------------------------------------------------------------*/

struct EContextFlag
{
	enum Type
	{
	    Culling			= 1 << 0,
	    DepthTesting	= 1 << 1,
	    Blending		= 1 << 2,
	    WriteColor		= 1 << 3,
	    WriteDepth		= 1 << 4,
	    WriteStencil	= 1 << 5
	};
};

/*--------------------------------------------------------------------------*/

struct EMaterialTechnique
{
	enum Type
	{
	    Default			= 0,
	    Instanced,
	    Font_Normal,
	    Font_Outline,
	    Primitive_NoTexture,
	    Count,
	    Default3D,					// primitive
	    Primitive_NoTexture_3D,		// primitive
	    None
	};
};

/*--------------------------------------------------------------------------*/

struct MaterialParameter
{
	std::string			m_name;
	uint		m_location;
};

/*--------------------------------------------------------------------------*/

struct UniformSampler
{
	std::string		name;
	std::string		textureName;
	GLTexture*		texturePtn;

	std::string		samplerName;
	void*			samplerPtn;

	UniformSampler() :
		texturePtn(NULL),
		samplerPtn(NULL)
	{}

	UniformSampler(const std::string& name, const std::string& textureName, const std::string& samplerName) :
		name(name),
		textureName(textureName),
		texturePtn(NULL),
		samplerName(samplerName),
		samplerPtn(NULL)
	{}
};

struct Context;

struct Technique
{
	const Context*				m_context;
	GLProgram*					m_program;
	GLShaderPtnVec				m_shaders;
	EMaterialTechnique::Type	m_techniqueType;
	std::string					m_techniqueMacroName;
	std::vector<UniformSampler> m_samplers;

	Technique() :
		m_context(NULL),
		m_program(NULL),
		m_techniqueType(EMaterialTechnique::None),
		m_techniqueMacroName("UNKNOWN")
	{}

	Technique(Context* context, GLProgram* program, EMaterialTechnique::Type type, const std::string& macroname) :
		m_context(context),
		m_program(program),
		m_techniqueType(type),
		m_techniqueMacroName(macroname)
	{}
};

typedef std::vector<Technique> TechniqueVec;

/*--------------------------------------------------------------------------*/

struct Context
{
	const std::string	m_name;
	GLenum				m_cullingMode;
	GLenum				m_blendSrc;
	GLenum				m_blendDst;
	GLenum				m_frontPolygonMode;
	GLenum				m_backPolygonMode;

	uint		m_flags;

	Context(const std::string& name) :
		m_name(name),
		m_flags(0),
		m_cullingMode(GL_NONE),
		m_blendSrc(GL_SRC_ALPHA),
		m_blendDst(GL_ONE_MINUS_SRC_ALPHA),
		m_frontPolygonMode(GL_FILL),
		m_backPolygonMode(GL_FILL)
	{}

};

typedef std::map<std::string, Context*> ContextPtnMap;

/*--------------------------------------------------------------------------*/

class Material
{
public:
	Material(const std::string& matfile);
	virtual ~Material();

	/*--------------------------------------------------------------------------*/

	uint		GetContextFlags(EMaterialTechnique::Type v) const;
	const std::string&	GetName() const;
	GLProgram*			GetProgram(EMaterialTechnique::Type v) const;
	const glm::vec3&	GetDiffuseColor() const;
	const float&		GetSpecularShininess() const;
	const float&		GetNormalMapStrength() const;
	const GLTextureMap&	GetTextures() const;
	const Technique*	GetTechnique(EMaterialTechnique::Type v) const;

private:

	/*--------------------------------------------------------------------------*/

	std::string			m_name;
	TechniqueVec		m_techniques;
	GLTextureMap		m_textures;

	/*--------------------------------------------------------------------------*/

	glm::vec3			m_diffuseColor;
	float				m_specularShininess;
	float				m_normalMap_strength;

	/*--------------------------------------------------------------------------*/

	void				LoadMaterialFromFile(const std::string& path);
	void				LoadContext(pugi::xml_node& contextNode, Context* context);
	void				LoadTechniques(pugi::xml_node& materialNode);
	void				LoadShader(pugi::xml_node& shaderNode, Technique& technique);
};

typedef std::map<std::string, Material*> MaterialPtnMap;